<!doctype html>

<head>
	<meta charset="UTF-8" />
	<meta name="format-detection" content="telephone=no" />
	<meta name="apple-mobile-web-app-capable" content="yes" />
	<meta name="viewport" content="user-scalable=no, viewport-fit=cover" />
	<title>无名杀</title>
	<script>
		if (typeof window.require == "function" && typeof window.process == "object" && typeof window.__dirname == "string") {
			// 使importMap解析node内置模块
			const builtinModules = require("module").builtinModules;
			if (Array.isArray(builtinModules)) {
				const importMap = {
					imports: {},
				};
				for (const module of builtinModules) {
					importMap.imports[module] = importMap.imports[`node:${module}`] = `./noname-builtinModules/${module}`;
				}
				const im = document.createElement("script");
				im.type = "importmap";
				im.textContent = JSON.stringify(importMap);
				document.currentScript.after(im);
			}
		}
	</script>
	<script>
		"use strict";
		(() => {
			window.onerror = function (msg, src, line, column, err) {
				let str = `错误文件: ${typeof src == "string" && src.length > 0 ? decodeURI(src) : "未知文件"}`;
				str += `\n错误信息: ${msg}`;
				str += `\n行号: ${line}`;
				str += `\n列号: ${column}`;
				if (err && err.stack) str += "\n" + decodeURI(err.stack);
				console.error(str);
				alert(str);
			};
			const STACK_REGEXP = /^\s*at .*(\S+:\d+|\(native\))/m;
			const errorList = [];
			function extractLocation(urlLike) {
				// 不存在地址信息的字符串
				if (!/:/.test(urlLike)) {
					return [urlLike];
				}

				// 捕获位置用到的正则
				const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/;
				const parts = regExp.exec(urlLike.replace(/[()]/g, ""));

				// @ts-ignore
				return [parts[1], parts[2] || void 0, parts[3] || void 0];
			}
			window.onunhandledrejection = async event => {
				event.promise.catch(error => {
					console.log(error);
					// 如果`error`是个错误，则继续处理
					if (error instanceof Error) {
						// 如果已经处理过该错误，则不再处理
						if (errorList.includes(error)) return;
						errorList.push(error);
						// 如果`error`拥有字符串形式的报错栈堆，且报错栈堆确实符合v8的stack
						if (typeof error.stack === "string" && STACK_REGEXP.test(error.stack)) {
							// 获取符合栈堆信息的字符串，一般来说就是从第二行开始的所有行
							// 为了处理eval的情况，故必须获取完行数
							let lines = error.stack.split("\n").filter(line => STACK_REGEXP.test(line));

							// 提供类型信息防止vscode报错
							/**
							 * @type {string | undefined}
							 */
							let fileName = void 0;

							/**
							 * @type {number | undefined}
							 */
							let line = void 0;

							/**
							 * @type {number | undefined}
							 */
							let column = void 0;

							// 从第一条开始遍历，一直遍历到不存在eval的位置
							for (let currentLine = 0; currentLine < lines.length; ++currentLine) {
								if (/\(eval /.test(lines[currentLine])) continue;

								let formatedLine = lines[currentLine]
									.replace(/^\s+/, "")
									.replace(/\(eval code/g, "(")
									.replace(/^.*?\s+/, "");

								const location = formatedLine.match(/ (\(.+\)$)/);
								if (location) formatedLine = formatedLine.replace(location[0], "");

								const locationParts = extractLocation(location ? location[1] : formatedLine);

								fileName = ["eval", "<anonymous>"].includes(locationParts[0]) ? void 0 : locationParts[0];
								line = Number(locationParts[1]);
								column = Number(locationParts[2]);
								break;
							}

							// @ts-ignore
							window.onerror(error.message, fileName, line, column, error);
						}
						// 反之我们只能不考虑报错文件信息，直接调用onerror
						else {
							try {
								// @ts-ignore
								let [_, src = void 0, line = void 0, column = void 0] = /at\s+.*\s+\((.*):(\d*):(\d*)\)/i.exec(error.stack.split("\n")[1]);
								if (typeof line == "string") line = Number(line);
								if (typeof column == "string") column = Number(column);
								// @ts-ignore
								window.onerror(error.message, src, line, column, error);
							} catch (e) {
								window.onerror(error.message, "", 0, 0, error);
							}
						}
					}
				});
			};
		})();
	</script>
	<script>
		"use strict";
		if (location.href.startsWith("http") && typeof window.initReadWriteFunction != "function" && !window.require && !window.__dirname) {
			window.initReadWriteFunction = function (game) {
				return new Promise((resolve, reject) => {
					fetch(`./checkFile?fileName=noname.js`)
						.then(response => {
							return response.json();
						})
						.then(result => {
							if (result?.success) {
								callback();
							} else {
								reject(result.errorMsg);
							}
						})
						.catch(reject);

					return;

					function callback() {
						/**
						 * 检查指定的路径是否是一个文件
						 *
						 * @param {string} fileName - 需要查询的路径
						 * @param {(result: -1 | 0 | 1) => void} [callback] - 回调函数；接受的参数意义如下:
						 *  - `-1`: 路径不存在或无法访问
						 *  - `0`: 路径的内容不是文件
						 *  - `1`: 路径的内容是文件
						 * @param {(err: Error) => void} [onerror] - 接收错误的回调函数
						 * @return {void} - 由于三端的异步需求和历史原因，文件管理必须为回调异步函数
						 */
						game.checkFile = function checkFile(fileName, callback, onerror) {
							fetch(`./checkFile?fileName=${fileName}`)
								.then(response => response.json())
								.then(result => {
									if (result) {
										if (result.success) {
											callback?.(1);
											return;
										}

										if (result.code === 404) {
											if (result.errorMsg === "不是一个文件") {
												callback?.(0);
												return;
											} else if (result.errorMsg === "文件不存在或无法访问") {
												callback?.(-1);
												return;
											}
										}
									}

									onerror?.(result?.errorMsg);
								})
								.catch(onerror);
						};

						/**
						 * 检查指定的路径是否是一个目录
						 *
						 * @param {string} dir - 需要查询的路径
						 * @param {(result: -1 | 0 | 1) => void} [callback] - 回调函数；接受的参数意义如下:
						 *  - `-1`: 路径不存在或无法访问
						 *  - `0`: 路径的内容不是目录
						 *  - `1`: 路径的内容是目录
						 * @param {(err: Error) => void} [onerror] - 接收错误的回调函数
						 * @return {void} - 由于三端的异步需求和历史原因，文件管理必须为回调异步函数
						 */
						game.checkDir = function checkDir(dir, callback, onerror) {
							fetch(`./checkDir?dir=${dir}`)
								.then(response => response.json())
								.then(result => {
									if (result) {
										if (result.success) {
											callback?.(1);
											return;
										}

										if (result.code === 404) {
											if (result.errorMsg === "不是一个文件夹") {
												return void callback?.(0);
											} else if (result.errorMsg === "文件夹不存在或无法访问") {
												return void callback?.(-1);
											}
										}
									}

									onerror?.(result?.errorMsg);
								})
								.catch(onerror);
						};

						game.readFile = function readFile(fileName, callback = () => {}, error = () => {}) {
							fetch(`./readFile?fileName=${fileName}`)
								.then(response => response.json())
								.then(result => {
									if (result?.success) {
										const data = result.data;

										/** @type {Uint8Array} */
										let buffer;
										if (typeof data == "string") {
											buffer = Uint8Array.fromBase64(data);
										} else if (Array.isArray(data)) {
											buffer = new Uint8Array(data);
										}

										callback(buffer.buffer);
									} else {
										error(result?.errorMsg);
									}
								})
								.catch(error);
						};

						game.readFileAsText = function readFileAsText(fileName, callback = () => {}, error = () => {}) {
							fetch(`./readFileAsText?fileName=${fileName}`)
								.then(response => response.json())
								.then(result => {
									if (result?.success) {
										callback(result.data);
									} else {
										error(result?.errorMsg);
									}
								})
								.catch(error);
						};

						game.writeFile = function writeFile(data, path, name, callback = () => {}) {
							game.ensureDirectory(path, () => {
								if (Object.prototype.toString.call(data) == "[object File]") {
									const fileReader = new FileReader();
									fileReader.onload = event => {
										game.writeFile(event.target.result, path, name, callback);
									};
									fileReader.readAsArrayBuffer(data, "UTF-8");
								} else {
									let filePath = path;
									if (path.endsWith("/")) {
										filePath += name;
									} else if (path == "") {
										filePath += name;
									} else {
										filePath += "/" + name;
									}

									fetch(`./writeFile`, {
										method: "post",
										headers: { "Content-Type": "application/json" },
										body: JSON.stringify({
											data: typeof data == "string" ? data : Array.prototype.slice.call(new Uint8Array(data)),
											path: filePath,
										}),
									})
										.then(response => response.json())
										.then(result => {
											if (result?.success) {
												callback();
											} else {
												callback(result?.errorMsg);
											}
										});
								}
							});
						};

						game.removeFile = function removeFile(fileName, callback = () => {}) {
							fetch(`./removeFile?fileName=${fileName}`)
								.then(response => response.json())
								.then(result => {
									callback(result.errorMsg);
								})
								.catch(error);
						};

						game.getFileList = function getFileList(dir, callback = () => {}, onerror) {
							fetch(`./getFileList?dir=${dir}`)
								.then(response => response.json())
								.then(result => {
									if (!result) {
										throw new Error("Cannot get available resource.");
									}

									if (result.success) {
										callback(result.data.folders, result.data.files);
									} else if (onerror) {
										onerror(new Error(result.errorMsg));
									}
								});
						};

						game.ensureDirectory = function ensureDirectory(list, callback = () => {}, file = false) {
							let pathArray = typeof list == "string" ? list.split("/") : list;
							if (file) {
								pathArray = pathArray.slice(0, -1);
							}
							game.createDir(pathArray.join("/"), callback, console.error);
						};

						game.createDir = function createDir(directory, successCallback = () => {}, errorCallback = () => {}) {
							fetch(`./createDir?dir=${directory}`)
								.then(response => response.json())
								.then(result => {
									if (result?.success) {
										successCallback();
									} else {
										errorCallback(new Error("创建文件夹失败"));
									}
								})
								.catch(errorCallback);
						};
						game.removeDir = function removeDir(directory, successCallback = () => {}, errorCallback = () => {}) {
							fetch(`./removeDir?dir=${directory}`)
								.then(response => response.json())
								.then(result => {
									if (result?.success) {
										successCallback();
									} else {
										errorCallback(new Error("创建文件夹失败"));
									}
								})
								.catch(errorCallback);
						};

						resolve();
					}
				});
			};
		}
	</script>
	<script src="game/update.js"></script>
	<script src="game/config.js"></script>
	<script src="game/package.js"></script>
	<script src="game/game.js"></script>
</head>
